package metamodel.misc;

import org.dom4j.DocumentHelper;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;

import java.util.HashMap;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import metamodel.visitor.ObjectGraphIterator;
import metamodel.visitor.DefaultGraphVisitor;
import metamodel.coreservices.StringMapper;
import metamodel.ClassNode;
import metamodel.DeepProperty;
import metamodel.CollectionRef;
import metamodel.FlatProperty;

/**
 * User: felix
 * Date: 13.06.2005
 */
public class XMLSerializer extends DefaultGraphVisitor {
    private ClassNode classNode;

    private PrintWriter output;
    StringMapper mapper=new SimpleStringMapper();
    private Document doc;
    HashMap object2element;



    public XMLSerializer(ClassNode classNode, OutputStream output) {
        this.classNode = classNode;
        this.output = new PrintWriter(output);

    }

    public void serialize(Object root){
        object2element=new HashMap(100);

        ObjectGraphIterator iter=new  ObjectGraphIterator(classNode,this, ObjectGraphIterator.TraversalMode.CHECK_TREE);
        doc= DocumentHelper.createDocument();
        iter.iterate(root);
         // Pretty print the document to System.out
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter writer = null;
        try {
            writer = new XMLWriter(System.out, format);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        try {
            writer.write( doc);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        output.flush();
    }

    public void visitObject(ClassNode classNode, DeepProperty property, Object value, Object parent) {
        Element element;
        element=((Element)object2element.get(parent)).addElement(classNode.getTypeName());
        object2element.put(value,element);
    }

    public void visitRoot(ClassNode classNode, Object data) {
        Element element = doc.addElement(classNode.getTypeName());
        object2element.put(data,element);
    }

    public void visitCollection(CollectionRef property, Object value, Object parent) {
           Element element = ((Element) object2element.get(parent)).addElement(property.getName());
           object2element.put(value,element);

    }

    public void visitFlatProperty(FlatProperty property, Object value, Object parent, boolean hasNext) {
        ((Element) object2element.get(parent)).addAttribute(property.getName(),mapper.toString(property, value));
    }

    public void visitCyclic(ClassNode classNode, DeepProperty property, Object value, Object parent, boolean hasNext) {}


    static class SimpleStringMapper implements StringMapper{
    public String toString(FlatProperty descriptor, Object value) {
        if (value==null) return "(null)";
        return value.toString();
    }

    public Object toObject(FlatProperty descriptor, String value) {
        // not needed here..
        return null;
    }
    }
}
